package util;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONWriter;
import entity.Region;
import enums.CrawlerYear;
import logger.MyLog;
import org.jsoup.Jsoup;
import org.jsoup.internal.StringUtil;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;

/**
 * 采集国家民政部县以上行政区划代码数据。
 *
 * @author 大定府羡民（QQ：1032694760）
 */
public class CrawlerUtils {

    private CrawlerUtils() {
        super();
    }

    public static void crawlMcaGov(CrawlerYear year) throws IOException {
        MyLog.enable(true);
        String filePath = "json/region_mca_%d.json".formatted(year.getValue());
        if (FileUtils.existsFile(filePath)) {
            MyLog.debug("%s year has been crawled: %s".formatted(year.getValue(), filePath));
            String json = FileUtils.readFromFile(filePath);
            List<Region> matchList = JSON.parseArray(json, Region.class);
            MyLog.debug("The total number resolved is: " + matchList.size());
            saveAsSQL(year, matchList);
            saveAsCSV(year, matchList);
            if (year == Arrays.stream(CrawlerYear.values()).sorted().toList().getLast()) {
                mergeAllData();
            }
            return;
        }
        SslUtils.ignoreSsl();
        String url = switch (year) {
            case MCA_1980 -> "https://www.mca.gov.cn/mzsj/tjbz/a/201713/201708040959.html";
            case MCA_1985 -> "https://www.mca.gov.cn/mzsj/tjbz/a/201713/201708220858.html";
            case MCA_1990 -> "https://www.mca.gov.cn/mzsj/tjbz/a/201713/201708041018.html";
            case MCA_1995 -> "https://www.mca.gov.cn/mzsj/tjbz/a/201713/201708220913.html";
            case MCA_2000 -> "https://www.mca.gov.cn/mzsj/tjbz/a/201713/201708220923.html";
            case MCA_2005 -> "https://www.mca.gov.cn/mzsj/tjbz/a/201713/201708220935.html";
            case MCA_2010 -> "https://www.mca.gov.cn/mzsj/tjbz/a/201713/201708220946.html";
            case MCA_2015 -> "https://www.mca.gov.cn/mzsj/tjbz/a/2015/201706011127.html";
            case MCA_2020 -> "https://www.mca.gov.cn/mzsj/xzqh/2020/20201201.html";
            case MCA_2021 -> "https://www.mca.gov.cn/mzsj/xzqh/2021/20211201.html";
            case MCA_2022 -> "https://www.mca.gov.cn/mzsj/xzqh/2022/202201xzqh.html";
            case MCA_2023 -> "https://www.mca.gov.cn/mzsj/xzqh/2023/202301xzqh.html";
            //case MCA_2024 -> throw new RuntimeException("The MCA has not yet released data for this year");
        };
        Document document = Jsoup.connect(url)
                .header("Accept", "*/*")
                .userAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0")
                .maxBodySize(0)
                .followRedirects(true)
                .ignoreHttpErrors(true)
                .ignoreContentType(true)
                .timeout(10000)
                .get();
        // <tr height=19 style='mso-height-source:userset;height:14.25pt'>
        //   <td height=19 class=xl659301 style='height:14.25pt'></td>
        //   <td class=xl719301>520525</td>
        //   <td class=xl719301><span style='mso-spacerun:yes'>   </span>纳雍县</td>
        //   <td class=xl719301></td>
        //   <td class=xl719301></td>
        //   <td class=xl699301></td>
        //   <td class=xl699301></td>
        //   <td class=xl639301></td>
        //   <td class=xl639301></td>
        //   <td class=xl159301></td>
        // </tr>
        // ...
        // <tr height=19 style='mso-height-source:userset;height:14.25pt'>
        //   <td height=19 class=xl659301 style='height:14.25pt'></td>
        //   <td class=xl719301></td>
        //   <td class=xl719301><span style='mso-spacerun:yes'>   </span>西沙区</td>
        //   <td class=xl709301></td>
        //   <td class=xl709301></td>
        //   <td class=xl699301></td>
        //   <td class=xl699301></td>
        //   <td class=xl639301></td>
        //   <td class=xl639301></td>
        //   <td class=xl159301></td>
        // </tr>
        Elements elements = document.select("tr");
        ArrayList<Region> matchList = new ArrayList<>();
        ArrayList<Region> ignoreList = new ArrayList<>();
        for (Element element : elements) {
            Elements selected = element.select("td");
            if (selected.size() >= 3) {
                String td0 = selected.get(0).text();
                String td1 = selected.get(1).text();
                String td2 = selected.get(2).text();
                String regionCode;
                String regionName;
                if (td0.matches("^[1-9]\\d{5}$")) {
                    regionCode = td0;
                    regionName = td1;
                } else {
                    regionCode = td1;
                    regionName = td2;
                }
                MyLog.debug("regionCode=%s, regionName=%s, selectedElement=%s".formatted(regionCode, regionName, selected));
                Region.Level level;
                String parentCode;
                List<String> skipKeywords = Arrays.asList("单位名称", "行政区划名称", "省直辖县级行政单位");
                if ((StringUtil.isBlank(regionCode) && StringUtil.isBlank(regionName)) ||
                    skipKeywords.contains(regionCode) || skipKeywords.contains(regionName)
                ) {
                    // 跳过无效的 tr/td 标签
                    continue;
                } else if (StringUtil.isBlank(regionCode)) {
                    // 三沙市下的区没有编码
                    level = Region.Level.UNKNOWN;
                    parentCode = "";
                } else if (regionCode.endsWith("0000")) {
                    level = Region.Level.PROVINCE;
                    parentCode = "000000";
                } else if (regionCode.endsWith("00")) {
                    level = Region.Level.CITY;
                    parentCode = regionCode.substring(0, 2) + "0000";
                } else {
                    level = Region.Level.COUNTY;
                    parentCode = regionCode.substring(0, 4) + "00";
                }
                Region region = new Region();
                region.name = regionName;
                region.code = regionCode;
                region.parentCode = parentCode;
                region.level = level.getValue();
                region.levelName = level.name();
                region.version = String.valueOf(year.getValue());
                if (regionCode.matches("^[1-9]\\d{5}$")) {
                    matchList.add(region);
                } else {
                    ignoreList.add(region);
                    // 三沙市下的区没有编码
                    if ("西沙区".equals(regionName)) {
                        Region xisha = new Region();
                        xisha.name = regionName;
                        xisha.code = "460321";
                        xisha.parentCode = "460300";
                        xisha.level = Region.Level.COUNTY.getValue();
                        xisha.levelName = Region.Level.COUNTY.name();
                        matchList.add(xisha);
                    } else if ("南沙区".equals(regionName)) {
                        Region nasha = new Region();
                        nasha.name = regionName;
                        nasha.code = "460322";
                        nasha.parentCode = "460300";
                        nasha.level = Region.Level.COUNTY.getValue();
                        nasha.levelName = Region.Level.COUNTY.name();
                        matchList.add(nasha);
                    }
                }
            }
        }
        MyLog.debug("The total number of regular matches is: " + matchList.size());
        FileUtils.saveToFile(filePath, JSON.toJSONString(matchList, JSONWriter.Feature.PrettyFormat));
        MyLog.debug("The total number of regular unmatched is: " + ignoreList.size());
        FileUtils.saveToFile("json/region_mca_%d_ignore.json".formatted(year.getValue()), JSON.toJSONString(ignoreList, JSONWriter.Feature.PrettyFormat));
        saveAsSQL(year, matchList);
        saveAsCSV(year, matchList);
        if (year == Arrays.stream(CrawlerYear.values()).sorted().toList().getLast()) {
            mergeAllData();
        }
    }

    private static void mergeAllData() {
        MyLog.enable(true);
        List<Region> list = new ArrayList<>();
        for (CrawlerYear year : CrawlerYear.values()) {
            List<Region> data = loadData(year);
            saveAsSQL(year, data);
            saveAsCSV(year, data);
            MyLog.debug("Loading data for year %s is: %s".formatted(year.getValue(), data.size()));
            for (Region it : data) {
                it.version = String.valueOf(year.getValue());
                list.removeIf(region -> it.code.equals(region.code));
                list.add(it);
            }
        }
        MyLog.debug("The total number of merged is: %s".formatted(list.size()));
        String filePath = "json/region_mca_data.json";
        FileUtils.saveToFile(filePath, JSON.toJSONString(list, JSONWriter.Feature.PrettyFormat));
        saveAsSQL("data", list);
        saveAsCSV("data", list);
    }

    private static List<Region> loadData(CrawlerYear year) {
        String filePath = "json/region_mca_%d.json".formatted(year.getValue());
        if (!FileUtils.existsFile(filePath)) {
            return new ArrayList<>();
        }
        String json = FileUtils.readFromFile(filePath);
        return JSON.parseArray(json, Region.class);
    }

    private static void saveAsSQL(CrawlerYear year, List<Region> regions) {
        saveAsSQL(String.valueOf(year.getValue()), regions);
    }

    private static void saveAsSQL(String version, List<Region> regions) {
        for (Region region : regions) {
            if (region.version == null) {
                region.version = version;
            }
        }
        regions.sort(Comparator.comparing(o -> o.code));
        StringBuilder sb = new StringBuilder();
        sb.append("SET FOREIGN_KEY_CHECKS = 0;\n");
        sb.append("REPLACE INTO `china_region_mca` (`name`, `code`, `parent_code`, `level`, `version`) VALUES\n");
        int i = 0, n = regions.size();
        for (Region region : regions) {
            sb.append("(");
            sb.append("'").append(region.name).append("', ");
            sb.append("'").append(region.code).append("', ");
            sb.append("'").append(region.parentCode).append("', ");
            sb.append("'").append(region.level).append("', ");
            sb.append("'").append(region.version).append("'");
            sb.append(i == n - 1 ? ");\n" : "), \n");
            i++;
        }
        sb.append("SET FOREIGN_KEY_CHECKS = 1;\n");
        FileUtils.saveToFile("sql/region_mca_%s.sql".formatted(version), sb.toString());
    }

    private static void saveAsCSV(CrawlerYear year, List<Region> regions) {
        saveAsCSV(String.valueOf(year.getValue()), regions);
    }

    private static void saveAsCSV(String version, List<Region> regions) {
        for (Region region : regions) {
            if (region.version == null) {
                region.version = version;
            }
        }
        regions.sort(Comparator.comparing(o -> o.code));
        StringBuilder sb = new StringBuilder();
        sb.append("name,code,parent_code,level,version\r\n");
        for (Region region : regions) {
            sb.append("\"").append(region.name).append("\",");
            sb.append("\"").append(region.code).append("\",");
            sb.append("\"").append(region.parentCode).append("\",");
            sb.append(region.level).append(",");
            sb.append("\"").append(region.version).append("\"\r\n");
        }
        FileUtils.saveToFile("csv/region_mca_%s.csv".formatted(version), sb.toString());
    }

}
